#include "helper.h"

#include "../include/mutex.h"
#include "../include/condvar.h"
#include <stdio.h>

namespace mix
{

struct CondVarImpl
{
#ifdef M_WIN
    HANDLE cond;
    HANDLE signalEvent;
    int waitNum;
    int wakeNum;
    Mutex locker;
#else
    pthread_cond_t cond;
#endif
};

#define IMPL ((CondVarImpl*)_impl)
#define IMPL_VAR(var) CondVarImpl* var = ((CondVarImpl*)_impl);

CondVar::CondVar( Mutex& mutex) : _mutex( mutex) {
    CondVarImpl* ca = new CondVarImpl();
    _impl = ca;
#ifdef M_WIN
    ca->waitNum = 0;
    ca->wakeNum = 0;
    ca->cond = ::CreateEvent( NULL, TRUE, FALSE, NULL);
    ca->signalEvent = ::CreateEvent( NULL, FALSE, FALSE, NULL);
    if ( ca->cond == 0 || ca->signalEvent) {
        MIX_LOG( LOG_CRITICAL, "CreateEvent failed.", ret);
    }
#else
    int ret = ::pthread_cond_init( &(ca->cond), NULL);
    if ( ret) {
        MIX_LOG( LOG_CRITICAL, "pthraad_cond_init failed. [%d]", ret);
    }
#endif
}

CondVar::~CondVar() {
    IMPL_VAR( ca);
#ifdef M_WIN
    ::CloseHandle( ca->cond); 
    ::CloseHandle( ca->signalEvent);
#else
	::pthread_cond_destroy( &ca->cond);
#endif
}


void CondVar::wait(){
    IMPL_VAR( ca);
#ifdef M_WIN
    ca->locker.lock();
    ++ca->waitNum;
    ca->locker.unlock();
    _mutex.unlock();
    while ( true) {
        ::WaitForSingleObject( ca->cond, INFINITE);
     	  ca->locker.lock();
     	  if ( ca->wakeNum > 0) {
     	      --ca->waitNum;
     	      --ca->wakeNum;
     	      if ( ca->wakeNum < 1) {
               ::ResetEvent( ca->cond);
               ::SetEvent( ca->signalEvent);
     	      }
            ca->locker.unlock();
            break;
        }
        ca->locker.unlock();
    }
    _mutex.lock(); 
#else
    int ret = pthread_cond_wait( &ca->cond, (pthread_mutex_t*)_mutex.getNative());
    if (ret) {
        MIX_LOG( LOG_CRITICAL,"prhread_cond_wait failed![code: %d]", ret); 
    }
#endif
}

bool CondVar::wait( long msec) {
    IMPL_VAR( ca);
#ifdef M_WIN 
    if ( msec <= 0)
     	  return false;
    ca->locker.lock();
    ++ca->waitNum;
    ca->locker.unlock();
    _mutex.unlock(); 
    while ( true) {
        DWORD ret = ::WaitForSingleObject( ca->cond, msec);
        ca->locker.lock();
        if ( ret == WAIT_TIMEOUT) {
         	  --ca->waitNum;
              --ca->wakeNum;
         	  ::SetEvent( ca->signalEvent);
         	  ca->locker.unlock();
         	  _mutex.lock();
         	  return false;
        } 
         
        if ( ca->wakeNum > 0) {
         	  --ca->waitNum;
         	  --ca->wakeNum;
         	  if ( ca->wakeNum < 1) {
         	   	  ::ResetEvent( ca->cond);
         	   	  ::SetEvent( ca->signalEvent);
         	  }
         	  ca->locker.unlock();
         	  break;
        } 
        ca->locker.unlock();
    }
    _mutex.lock();
     return true;
#else
	struct timespec ts;
    if ( !futureAbsTime( msec, ts))
        return false;

	int r = pthread_cond_timedwait( &ca->cond,
			(pthread_mutex_t*)_mutex.getNative(), &ts);
	if ( r == ETIMEDOUT)	// 超时
		return false;
	return true;
#endif
}

void CondVar::signal() {
    IMPL_VAR( ca);
#ifdef M_WIN 
    ca->locker.lock();
    if ( ca->waitNum > 0) { 
     	  ca->wakeNum = 1;
        ::SetEvent( ca->cond);
        ca->locker.unlock();
        ::WaitForSingleObject( ca->signalEvent, INFINITE);
    }else
     	  ca->locker.unlock();
#else
	::pthread_cond_signal( &ca->cond);
#endif
}

void CondVar::signalAll() {
    IMPL_VAR( ca);
#ifdef M_WIN
    ca->locker.lock();
    if ( ca->waitNum < 1)
     	ca->locker.unlock();
    else {
     	ca->wakeNum = ca->waitNum;
     	::SetEvent( ca->cond);
     	ca->locker.unlock();
     	::WaitForSingleObject( ca->signalEvent, INFINITE);
    }
#else
	::pthread_cond_broadcast( &ca->cond);
#endif
}

void* CondVar::getNative() {
    IMPL_VAR( ca);
	return &ca->cond;
}



}	// namespace mix
